/*
 * Copyright 2004-2014 SmartBear Software
 *
 * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
 * versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://ec.europa.eu/idabc/eupl
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
 * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the Licence for the specific language governing permissions and limitations
 * under the Licence.
*/

package org.syntax.jedit;

import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

/**
 * Class with several utility functions used by the text area component.
 *
 * @author Slava Pestov
 * @version $Id$
 */
public class TextUtilities {
    /**
     * Returns the offset of the bracket matching the one at the specified offset
     * of the document, or -1 if the bracket is unmatched (or if the character is
     * not a bracket).
     *
     * @param doc    The document
     * @param offset The offset
     * @throws BadLocationException If an out-of-bounds access was attempted on the document
     *                              text
     */
    public static int findMatchingBracket(Document doc, int offset) throws BadLocationException {
        if (doc.getLength() == 0) {
            return -1;
        }
        char c = doc.getText(offset, 1).charAt(0);
        char cprime; // c` - corresponding character
        boolean direction; // true = back, false = forward

        switch (c) {
            case '(':
                cprime = ')';
                direction = false;
                break;
            case ')':
                cprime = '(';
                direction = true;
                break;
            case '[':
                cprime = ']';
                direction = false;
                break;
            case ']':
                cprime = '[';
                direction = true;
                break;
            case '{':
                cprime = '}';
                direction = false;
                break;
            case '}':
                cprime = '{';
                direction = true;
                break;
            default:
                return -1;
        }

        int count;

        // How to merge these two cases is left as an exercise
        // for the reader.

        // Go back or forward
        if (direction) {
            // Count is 1 initially because we have already
            // `found' one closing bracket
            count = 1;

            // Get text[0,offset-1];
            String text = doc.getText(0, offset);

            // Scan backwards
            for (int i = offset - 1; i >= 0; i--) {
                // If text[i] == c, we have found another
                // closing bracket, therefore we will need
                // two opening brackets to complete the
                // match.
                char x = text.charAt(i);
                if (x == c) {
                    count++;
                }

                // If text[i] == cprime, we have found a
                // opening bracket, so we return i if
                // --count == 0
                else if (x == cprime) {
                    if (--count == 0) {
                        return i;
                    }
                }
            }
        } else {
            // Count is 1 initially because we have already
            // `found' one opening bracket
            count = 1;

            // So we don't have to + 1 in every loop
            offset++;

            // Number of characters to check
            int len = doc.getLength() - offset;

            // Get text[offset+1,len];
            String text = doc.getText(offset, len);

            // Scan forwards
            for (int i = 0; i < len; i++) {
                // If text[i] == c, we have found another
                // opening bracket, therefore we will need
                // two closing brackets to complete the
                // match.
                char x = text.charAt(i);

                if (x == c) {
                    count++;
                }

                // If text[i] == cprime, we have found an
                // closing bracket, so we return i if
                // --count == 0
                else if (x == cprime) {
                    if (--count == 0) {
                        return i + offset;
                    }
                }
            }
        }

        // Nothing found
        return -1;
    }

    /**
     * Locates the start of the word at the specified position.
     *
     * @param line The text
     * @param pos  The position
     */
    public static int findWordStart(String line, int pos, String noWordSep) {
        char ch = line.charAt(pos - 1);

        if (noWordSep == null) {
            noWordSep = "";
        }
        boolean selectNoLetter = (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1);

        int wordStart = 0;
        for (int i = pos - 1; i >= 0; i--) {
            ch = line.charAt(i);
            if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)) {
                wordStart = i + 1;
                break;
            }
        }

        return wordStart;
    }

    /**
     * Locates the end of the word at the specified position.
     *
     * @param line The text
     * @param pos  The position
     */
    public static int findWordEnd(String line, int pos, String noWordSep) {
        char ch = line.charAt(pos);

        if (noWordSep == null) {
            noWordSep = "";
        }
        boolean selectNoLetter = (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1);

        int wordEnd = line.length();
        for (int i = pos; i < line.length(); i++) {
            ch = line.charAt(i);
            if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)) {
                wordEnd = i;
                break;
            }
        }
        return wordEnd;
    }
}
